package com.varian.redis.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.varian.redis.aspect.VarianCacheAspect;
import com.varian.redis.message.RedisMessageDelegate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;

/**
 * @author ben
 * @since 2024/6/23
 */
@Slf4j
@EnableCaching
@AutoConfiguration
public class RedisAutoConfig {

    private final JdkSerializationRedisSerializer keySerializer = new JdkSerializationRedisSerializer();

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        template.setValueSerializer(keySerializer);
        template.setHashValueSerializer(keySerializer);
        log.info("initialed RedisTemplate And RedisUtil.");
        return template;
    }

    @Bean
    @SuppressWarnings({"rawtypes", "unchecked"})
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory, List<RedisMessageDelegate> messageDelegates) {
        RedisMessageListenerContainer listenerContainer = new RedisMessageListenerContainer();
        listenerContainer.setConnectionFactory(connectionFactory);
        CollUtil.forEach(messageDelegates, (delegate, index) ->
                listenerContainer.addMessageListener(((message, pattern) -> {
                            Object deserialized = keySerializer.deserialize(message.getBody());
                            String topic = StrUtil.str(pattern, StandardCharsets.UTF_8);
                            delegate.onMessage(topic, deserialized);
                        }),
                        delegate.getTopic()));
        log.info("initialed RedisMessageListenerContainer And MessageDelegate. {}", CollUtil.size(messageDelegates));
        return listenerContainer;
    }

    @Bean
    public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
        return builder -> {
            RedisCacheConfiguration cacheConfiguration = builder.cacheDefaults();
            cacheConfiguration.entryTtl(Duration.ofHours(1));
            builder.cacheDefaults(cacheConfiguration);
        };
    }

    @Bean
    @ConditionalOnBean(RedisTemplate.class)
    public CacheStarted cacheStarted() {
        return new CacheStarted();
    }

    @Bean
    public DefaultPointcutAdvisor varianCacheAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("@annotation(com.varian.redis.annotation.VarianCache) || @annotation(com.varian.redis.annotation.VarianCacheEvict) || @annotation(com.varian.redis.annotation.VarianCachePut)");
        return new DefaultPointcutAdvisor(pointcut, new VarianCacheAspect());
    }

}
